import traceback
import arrow

from wass.settings import DETAIL_LINK
from models.task import ScanEventActions, ScanTaskSessions
from models.target import Targets
from models.warn import WebReceivers, WebInboxMails
from models.user import OutUrls
from models.retry_request import RetryRequests
from models.task import ScanEvents

from apps.target.serializer import SerializerTargetGroup
from apps.warn_center.serializer import WarningSerializer, get_host_addr, FakeRequest
from apps.notify.notify_log import logger
from apps.notify.failed import FAILED_CONDITIONS
from apps.task.serializer import TaskSessionExtraSerializer

from utilities.errors import PushNotificationError
from utilities.custom_exception import EmailSendError
from utilities.scanner import BatchRemoveTask
from utilities.utils import iso_format_to_local, get_clean_dict
from utilities.email.wass_email import WassEmailSender
from utilities.scanner import BatchRemoveTaskSession
from utilities.utils import clear_dict
from utilities.api_send_message import send_message
from utilities.enums import ScanEventName, ScanJobName, SessionStatusName
from utilities.log import log_info


def do_destroy_tasks(task_id_list):
    BatchRemoveTask(task_id_list).batch_remove()


def do_set_target_title(target_id, title):
    Targets.objects(target_id=target_id).update(target_title=title)


def do_set_vul_verification(task_id, event_id, verification_detail):
    if verification_detail:
        new_traffics = []
        for item in verification_detail.get('traffics', []):
            new_traffics.append(clear_dict(item))
        verification_detail['traffics'] = new_traffics
        ScanEventActions.objects(event_id=event_id).update(
            verification=2, verification_detail=verification_detail
        )
    else:
        ScanEventActions.objects(event_id=event_id).update(verification=3)
    do_destroy_tasks([task_id])


def do_send_completed_email(task, obj_target, obj_target_group, obj_task_session, content):
    task_session_id = obj_task_session.task_session_id
    addition = task.get('addition', {})
    task_type = addition.get('task_type')
    target_id = obj_target.target_id
    user_id = obj_target.user_id
    task_config = obj_target_group.task_config
    if obj_target.task_config.is_solo:
        task_config = obj_target.task_config

    serializer = WarningSerializer(
        data={
            'task_session_id': task_session_id,
            'target_group_id': obj_target_group.target_group_id,
            'target_id': target_id,
            'task_type': task_type,
            'content': content.get('warning_content', ''),
            'user_id': user_id
        }
    )
    serializer.is_valid(raise_exception=True)
    serializer.save()

    receiver_email_list = [
        item.receiver_email
        for item in WebReceivers.objects.find(
            {'receiver_id': {'$in': task_config.receiver_list}}
        )
    ]
    if not receiver_email_list:
        return

    args = {
        'to': receiver_email_list,
        'title': f"任务组 '{obj_target_group.target_group_name}'发现网站{content.get('email_exception_name', '')}",
        'body': {
            '%username%': '',
            '%target_name%': obj_target.target_name or obj_target.target_title,
            '%target_url%': obj_target.target_url,
            '%total%': content.get('total', 0),
            '%end_at%': iso_format_to_local(
                arrow.get(obj_task_session.ended_at).isoformat()
            ),
            '%task_type_name%': content.get('task_type_name', ''),
            '%content%': content.get('email_content'),
            '%link%': DETAIL_LINK.format(
                host=get_host_addr(),
                target_id=target_id,
                task_type=task_type,
                task_session_id=task_session_id,
                target_url=obj_target.target_url,
            ),
        },
    }

    try:
        WassEmailSender().send(content.get('template'), args)
    except Exception as e:
        raise EmailSendError(e)


def do_send_completed_inbox_mail(task, content, is_periodic):
    target_id = task.get('target_id')
    obj_target = Targets.objects.filter(target_id=target_id).first()

    task_type_name = content.get('task_type_name', 'unknown')
    trigger_type = '周期性' if is_periodic else '单次'

    s0 = (
        f"您的{obj_target.target_name or obj_target.target_title}({obj_target.target_url})"
        f"业务系统已完成{task_type_name}{trigger_type}任务扫描，"
        f"{content.get('inbox_content')}"
    )
    WebInboxMails(
        title=f"{task_type_name}{trigger_type}任务扫描完成",
        content=s0,
        task_session_id=task.get('last_task_session_id', ''),
        user_id=task.get('user_id', ''),
    ).save()


def do_disable_target_groups(obj_target_group_list):
    for obj_target_group in obj_target_group_list:
        try:
            data = obj_target_group.to_dict()
            data['target_urls'] = SerializerTargetGroup.get_target_urls(obj_target_group)

            obj_target_group.enable = False
            serializer = SerializerTargetGroup(data=data, context={'request': FakeRequest()}, instance=obj_target_group)
            serializer.is_valid(raise_exception=True)
            serializer.update(obj_target_group, serializer.validated_data)
            obj_target_group.update(enable=False)
        except Exception as e:
            logger.error(e)
            traceback.print_exc()


def do_remove_task_session(task_session_id):
    BatchRemoveTaskSession([task_session_id]).batch_remove()


def do_push_completed(request_args):
    allow_notification_keys = (
        'notification_id', 'notified_at', 'notify_type',
        'notify_type_name', 'task_id', 'task_session_id', 'task', 'events')
    allow_task_keys = ('target_url', 'target_id', 'addition')
    allow_addition_keys = ('task_type', '')
    allow_event_keys = ('event_id', 'event_name', 'detail')

    user_id = request_args.get('task', {}).get('user_id')
    obj_url = OutUrls.objects.filter(user_id=user_id).first()
    if not obj_url:
        return

    event_list = []
    request_args = get_clean_dict(request_args, allow_notification_keys)
    request_args['task'] = get_clean_dict(request_args.get('task', {}), allow_task_keys)
    request_args['task']['addition'] = get_clean_dict(request_args['task'].get('addition', {}), allow_addition_keys)
    # request_args['events'] = [
    #     get_clean_dict(item, allow_event_keys) for item in request_args.get('events', [])
    #     if item['event_name'] != ScanEventName.statistics.value and item['job_name'] != ScanJobName.appscan.value
    # ]
    for item in ScanEvents.objects.filter(task_session_id=request_args.get('task_session_id')):
        if item.event_name == ScanEventName.statistics.value and item.job_name == ScanJobName.appscan.value:
            continue
        tmp_item = get_clean_dict(item.to_dict(), allow_event_keys)
        event_list.append(tmp_item)

    request_args['events'] = event_list
    try:
        is_ack, _ = send_message(request_args, obj_url)
        assert is_ack
    except Exception:
        do_save_request(body=request_args, url_id=obj_url.url_id)
        raise PushNotificationError()


def do_save_request(url=None, method=None, headers=None, body=None, url_id=None):
    obj_request = RetryRequests(url=url, method=method, headers=headers, body=body, url_id=url_id).save()

    log_info(f"Save request {obj_request.request_id}")


def do_save_current_version(task_session_id, data):
    instance = TaskSessionExtraSerializer.Meta.model.objects.filter(task_session_id=task_session_id).first()
    serializer = TaskSessionExtraSerializer(instance, data=data)
    serializer.is_valid(raise_exception=True)
    serializer.save()


def do_set_is_failed(obj_task_session):
    for conditions in FAILED_CONDITIONS:
        failed_reason = conditions(obj_task_session)
        if failed_reason:
            instance = TaskSessionExtraSerializer.Meta.model.objects(
                task_session_id=obj_task_session.task_session_id).first()
            serializer = TaskSessionExtraSerializer(instance, data={
                'task_session_id': obj_task_session.task_session_id,
                'user_id': obj_task_session.user_id,
                'target_id': obj_task_session.target_id,
                'reason': failed_reason,
                'status': 10,
                'status_name': SessionStatusName.failed.value
            }, partial=True)
            serializer.is_valid(raise_exception=True)
            serializer.save()
            break
